package com.szeldon.declatest.runners;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import org.apache.commons.cli.ParseException;

import com.szeldon.declatest.declaration.TestsSuiteDeclaration;
import com.szeldon.declatest.declaration.reader.XMLDeclarationReader;
import com.szeldon.declatest.declaration.reader.exceptions.TestSuiteFileException;
import com.szeldon.declatest.declaration.reader.settings.JMSSettingsReaderPlugin;
import com.szeldon.declatest.eval.plugins.DefaultEvaluatorPlugin;
import com.szeldon.declatest.eval.plugins.XPathEvaluatorPlugin;
import com.szeldon.declatest.exec.AbstractTestsSuiteExecutor;
import com.szeldon.declatest.exec.ITestSuiteExecutorListener;
import com.szeldon.declatest.exec.SingleThreadExecutor;
import com.szeldon.declatest.exec.TestSuiteExecHistory;
import com.szeldon.declatest.exec.events.TestSuiteExecEvent;
import com.szeldon.declatest.exec.plugins.JMSDoScenarioPartExecutorPlugin;
import com.szeldon.declatest.reports.ReportGenerator;
import com.szeldon.declatest.reports.TestSuiteReportData;
import com.szeldon.declatest.reports.exceptions.GeneratingReportException;
import com.szeldon.declatest.reports.plugins.HtmlReportGeneratorPlugin;
import com.szeldon.utils.ValuesUtils;

/**
 * Starter class for Declaretest project.
 * 
 * @author szeldon
 * 
 */
public class Main {

    private static Logger logger = Logger.getLogger(Main.class.getName());

    private static ITestSuiteExecutorListener executorListener = new ITestSuiteExecutorListener() {

        @Override
        public void actionPerformed(TestSuiteExecEvent event) {
            logger.info(event.toString());
        }
    };

    private String[] args;

    public Main(String[] args) {
        setupLogger();
        this.args = args;

    }

    public static void main(String[] args) {
        Main main = new Main(args);
        main.start();
    }

    /**
     * Starts running a Declatest basing on previously added args.
     */
    public void start() {
        logger.info("Starting Declatest.");

        CLIInterpreter interpreter = interpretArgs(args);

        if (interpreter != null) {
            // TODO: currently works with only one test suite file
            TestsSuiteDeclaration testSuiteDeclaration = readTestSuiteDeclaration(interpreter);
            if (testSuiteDeclaration != null) {
                SingleThreadExecutor executor = new SingleThreadExecutor();
                List<TestSuiteReportData> testSuiteReportData = executeTestSuite(executor, testSuiteDeclaration);

                if (testSuiteReportData != null) {
                    generateReport(testSuiteReportData, testSuiteDeclaration, executor, interpreter);
                } else {
                    logger.severe("Something's really wrong because test suite executor didn't return anything. "
                            + "Probably a bug!");
                }

            } else {
                logger.severe("Something's really wrong because test suite declaration's reader didn't return "
                        + "any result. Probably a bug!");
            }
        }

        logger.info("Declatest stopped.");
    }

    private void setupLogger() {
        System.out.println("Setting up logger.");
        String fileSeparator = System.getProperty("file.separator");

        Calendar calendar = new GregorianCalendar();
        String year = "" + calendar.get(Calendar.YEAR);
        String month = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.MONTH) + 1);
        String day = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.DAY_OF_MONTH));
        String hour = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.HOUR_OF_DAY));
        String minute = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.MINUTE));
        String second = ValuesUtils.convertToTwoDigitString(calendar.get(Calendar.SECOND));

        Handler fileHandler;
        try {
            fileHandler = new FileHandler("logs" + fileSeparator + "test-" + year + month + day + "-" + hour + ""
                    + minute + "" + second + ".log");
            fileHandler.setFormatter(new SimpleFormatter());
            Logger.getLogger("").addHandler(fileHandler);
            Logger.getLogger("com.szeldon.declatest").setLevel(Level.FINEST);
        } catch (SecurityException e) {
            System.out.println("Security error during creating log file. " + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("IO error during creating log file. " + e.getMessage());
            e.printStackTrace();
        }

    }

    private CLIInterpreter interpretArgs(String[] args) {
        CLIInterpreter interpreter = new CLIInterpreter(args);
        try {
            interpreter.interpretArgs();
        } catch (ParseException e) {
            logger.log(Level.SEVERE, "Error during parsing command line arguments. ", e);
            return null;
        }

        return interpreter;
    }

    private void generateReport(List<TestSuiteReportData> executeTestSuite, TestsSuiteDeclaration testSuiteDeclaration,
            AbstractTestsSuiteExecutor executor, CLIInterpreter interpreter) {
        ReportGenerator generator = new ReportGenerator();
        // TODO: at the moment only HTML reports are supported
        generator.addGeneratorPlugin(new HtmlReportGeneratorPlugin());

        try {
            for (TestSuiteExecHistory currentHistory : executor.getHistoriesList()) {

                generator.generateReport(testSuiteDeclaration, currentHistory, executor.getProperties(),
                        interpreter.getXmlReportDataDirectory(), interpreter.getReportPluginsDirectory());
            }
        } catch (IOException e) {
            logger.log(Level.SEVERE, "IO problem during generating report. ", e);
        } catch (GeneratingReportException e) {
            logger.log(Level.SEVERE, "Error during generating report. ", e);
        }
    }

    private TestsSuiteDeclaration readTestSuiteDeclaration(CLIInterpreter interpreter) {
        // TODO: At the moment it's using only XML reader because there aren't any other readers
        XMLDeclarationReader reader = new XMLDeclarationReader(interpreter.getTestSuiteDeclarationFilename());
        // TODO: At the moment it's using only JMS settings reader because there aren't any other readers
        reader.addSettingsReaderPlugin(new JMSSettingsReaderPlugin());

        try {
            return reader.readTestsSuiteDeclaration();
        } catch (FileNotFoundException e) {
            logger.log(Level.SEVERE, "Can't find a file connected with test suite declaration.", e);
            return null;
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Problem during reading test suite declaration.", e);
            return null;
        } catch (TestSuiteFileException e) {
            logger.log(Level.SEVERE, "Bad file with test suite declaration.", e);
            return null;
        }
    }

    private List<TestSuiteReportData> executeTestSuite(SingleThreadExecutor executor,
            TestsSuiteDeclaration testSuiteDeclaration) {

        // TODO: At the moment it's using only JMS do scenario part executor because there aren't any other
        executor.addDoScenarioPartExecutorPlugin(new JMSDoScenarioPartExecutorPlugin());
        // TODO: At the moment it's using default evaluator because there aren't any other
        executor.addEvaluatorPlugin(new DefaultEvaluatorPlugin());
        executor.addEvaluatorPlugin(new XPathEvaluatorPlugin());
        executor.addTestsSuiteDeclaration(testSuiteDeclaration);
        executor.setListener(executorListener);

        executor.getHistoriesList();

        return executor.execute();
    }

}
